Garbage Collector
C# 및 Unity 에서 주요한 기능 중 하나인 Garbage Collector 에 대해 정리해보았습니다.
Garbage Collector(GC)
프로그램에서 더 이상 사용되지 않는 메모리를 자동으로 정리하는 소프트웨어 구성요소로
애플리케이션의 메모리 할당 및 해제를 관리합니다.
GC 가 메모리에 할당된 오브젝트를 지속적으로 추적하고, 해당 오브젝트가 더이상 사용되지 않을 때 자동으로 메모리 상에서 해제합니다. 사용하지 않는 메모리를 찾아서 비우는 프로세스를 가비지 컬렉션(Garbage Collection)이라 합니다.
일반적으로 사용되는 GC 알고리즘에는 세대와 참조 카운팅을 이용하여 오브젝트를 추적합니다.(Unity 에서는 세대와 참조 카운팅이 없습니다)
GC 의 장단점을 이해하여 그에 따른 영향을 염두에 두고 프로그램을 설계하는 것이 중요합니다.
GC 장점
프로그래머가 직접 복잡하고 오류가 발생하기 쉬운 메모리 할당 및 해제를 할 필요가 없기 때문에 프로그래밍을 더 쉽고 효율적으로 만들 수 있습니다.
메모리 누수(프로그램이 더 이상 사용하지 않는 메모리를 해제하지 않을 때 발생)나 메모리 관련 문제(이미 메모리에서 해제된 개체에 접근하는 것)를 방지하여 프로그램의 성능을 향상시키는 데 도움이 될 수 있습니다.
GC 단점
GC 가 실행될 때 프로그램은 일시 중지되어 메모리를 정리하는데 이로 인해 프로그램의 성능이 저하될 수 있습니다.
GC 는 더 이상 사용되지 않는 개체를 항상 정확하게 식별할 수 있는 것은 아니며, 이로 인해 프로그램에 메모리 누수가 발생할 수 있습니다.
Unity 에서의 GC
Unity 는 스크립트가 관리되는 힙(Managed heap)에 메모리를 할당하려고 할 때 메모리가 충분하지 않으면 Unity GC 를 실행합니다.
GC 가 실행되면 힙의 모든 오브젝트를 검사하고 애플리케이션에서 더 이상 레퍼런스가 없는 오브젝트를 삭제하도록 표시합니다. 그런 다음 Unity는 레퍼런스가 없는 오브젝트를 삭제하여 메모리를 확보합니다.(Mark-and-sweep algorithm)
Unity 에서는 Boehm-Demers-Weiser GC를 사용합니다.
Boehm-Demers-Weiser(Boehm) algorithm
비세대 기반
일반적인 GC 알고리즘에서는 GC가 힙 메모리 영역의 새대를 나누고 그에 따른 영역을 따로 관리합니다. 하지만 비세대 기반은 GC 가 전체 힙 영역을 확인해야 합니다.
GC 구현이 더 간단하고 일관된 성능을 제공할 수 있다는 장점이 있지만 효율성은 떨어질 수 있습니다.
비압축화
GC 가 사용하지 않는 메모리를 해제하고 난 뒤 메모리 단편화가 발생한 힙의 여유 공간에 오브젝트 재배치를 하지 않는 것을 말합니다. 압축화하는 것에 비해 GC 의 작업이 덜 복잡하다는 장점이 있지만 힙 메모리를 효율적으로 사용하지 못할 가능성이 있습니다.
stop-the-world
GC 가 동작할 때 메인 CPU 스레드를 중지(stop the world)하는 방식으로 작업을 처리합니다. GC 의 동작이 끝난 후 CPU 스레드가 재개되므로 GC 의 작업량이 많아지면 애플리케이션 성능에 영향을 줄 수 있습니다.
이런 문제점을 해결하기 위해 Unity 에서는 incremetal GC 라는 옵션을 제공합니다.
incremental GC
Garbage collection 작업을 분산하여 여러 프레임에 걸쳐 수행합니다. Garbage collection 전체 수행 시간은 동일하지만 GC Spike 가 줄어듭니다.
GC Spike(Garbage Collector Spike)
GC 가 많은 양의 메모리를 한번에 처리할 때 CPU 사용량이 증가하거나 컴퓨터가 잠깐 멈추는 경우가 발생하는 경우를 말합니다. 이런 GC Spike 는 일관된 프레임 속도를 유지되지 않아서 프레임 수가 일정하게 유지되어야 하는 실시간 게임이나 어플리케이션에서 성능 이슈를 초래할 수 있습니다.